#include"lrc.h"

int lrcfilesize;

int
lrc(int lrcfd)
{
	int get_lrcfile_size(int);
	struct lrccontain* read_lrcfile(int,long int *);
	struct lrccontain *clean_lrcfile(struct lrccontain *);
	struct lrctimeandchar *separate_time(struct lrccontain *);
	struct lrctimeandchar *sort_lrcfile(struct lrctimeandchar *);
	struct lrctimeandchar *copy_contain(struct lrctimeandchar *,struct lrctimeandchar *);
	struct lrctimeandchar *timesort(struct lrctimeandchar *,struct lrctimeandchar *);
	struct lrctimeandchar *get_timetosleep(struct lrctimeandchar *);
	lrcfilesize=get_lrcfile_size(lrcfd);
	struct lrccontain *head,*head2;
	struct lrctimeandchar *head3,*head4,*head5;
	long int offset_value;
	head=read_lrcfile(lrcfd,&offset_value);
	usleep(offset_value);
	head2=clean_lrcfile(head);
	head3=separate_time(head2);
	head4=sort_lrcfile(head3);
	head5= get_timetosleep(head4);
printf("\n");	
	do{
		nanosleep(&(head5->time),NULL);
		printf("%s \n",head5->character);
		head5=head5->next;
	}while(head5->next!=NULL);
		printf("%s \n",head5->character);
	printf("\n");

	exit(0);
}
int get_lrcfile_size(int fd)
{
	struct stat *buffers;
	if((buffers=(struct stat *)malloc(sizeof(struct stat)))==NULL){
		fprintf(stderr,"malloc() error in get_lrcfile_size() \n");
		exit(1);
	}
	if(fstat(fd,buffers)==-1){
		fprintf(stderr,"lstat() error in get_lrcfile_size() \n");
		exit(1);
	}
	free(buffers);
	return (buffers->st_size);
}

struct lrccontain* read_lrcfile(int fd,long int *offset_value)
{
	char buffer[lrcfilesize+1],*ptrline,*ptrperline,*ptr;
	struct lrccontain *head,*temp;
	temp=(struct lrccontain *)malloc(sizeof(struct lrccontain));
	char offset[9];
	offset[0]='[';
	offset[1]='o';
	offset[2]='f';
	offset[3]='f';
	offset[4]='s';
	offset[5]='e';
	offset[6]='t';
	offset[7]=':';
	offset[8]='\0';
	char time[10];
	int i,j,lrclen;

	head=temp;

	if(read(fd,buffer,lrcfilesize)==-1){
		fprintf(stderr,"read() error in read_lrcfile() \n");
		return NULL;
	}

	buffer[lrcfilesize]='\0';
	for(ptr=&buffer[0];*ptr!='\0';ptr++){
		if(strncmp(ptr,offset,8)==0){
			ptr +=8;
			for(i=0;i<10;i++){
				time[i]=ptr[i];
				if(ptr[i]==']')
					break;
			}
			time[i]='\0';
			
			break;
		}
	}

	*offset_value=atoi(time); 

	for(i=0;!(buffer[i]=='['&&isdigit(buffer[i+1]));i++);
	ptrline=&buffer[i];
	ptrperline=ptrline;

	lrclen=strlen(ptrline);
	ptr=&buffer[i];
	
	for(i=0,j=0;i<=lrclen;i++){
		if(ptrline[i]=='\n'&&ptrline[i+1]=='['){
		strncpy(temp->oneline,ptr,i-j);
		temp->oneline[i-j]='\0';
		ptr=ptr+i-j;
		j=i;
		
		temp->next=(struct lrccontain *)malloc(sizeof(struct lrccontain));
		temp=temp->next;
		}
	}
	temp->next=NULL;
	strcpy(temp->oneline,ptr);
	return head;
}
struct lrccontain *clean_lrcfile(struct lrccontain *head)
{
	char *lrcptr,contain[lrclength];

	int i;
	struct lrccontain *temp=head;

	do{
		lrcptr=strchr(head->oneline,'[');
		strcpy(contain,lrcptr);
		strcpy(head->oneline,contain);
		for(i=0;i<lrclength;i++){
			if(head->oneline[i]=='\r'||head->oneline[i]=='\n'){
				head->oneline[i]='\0';
				break;
			}
		}

		head=head->next;

	}while(head->next!=NULL);
	lrcptr=strchr(head->oneline,'[');
	strcpy(contain,lrcptr);
	strcpy(head->oneline,contain);
	for(i=0;i<lrclength;i++){
		if(head->oneline[i]=='\r'||head->oneline[i]=='\n'){
			head->oneline[i]='\0';
			break;
		}
	}

	return temp;
}
struct lrctimeandchar *separate_time(struct lrccontain *head)
{
	struct lrctimeint * lrctimechar_to_lrctimeint(int , char *);
	int count,dot;
	struct lrctimeandchar *lrchead,*temp;
	lrchead=(struct lrctimeandchar *)malloc(sizeof(struct lrctimeandchar));
	temp=lrchead;
	struct lrctimeint *lrctime;
	struct lrccontain *headtemp;
	headtemp=head;
	char lrctimeghost[11],*templine;
	do{
		dot=0;
		templine=strrchr(head->oneline,']');
		strcpy(lrchead->character,templine+1);
		for(count=0;head->oneline[count]!='\0';count++){
			if(head->oneline[count]=='.'){
				dot=1;
				continue;
			}
			if(head->oneline[count]==']'){
				if(dot==0){
					strncpy(lrctimeghost,&(head->oneline[count-6]),6);
				}else{
					strncpy(lrctimeghost,&(head->oneline[count-9]),9);
				}
				lrctime=lrctimechar_to_lrctimeint(dot,lrctimeghost);
				dot=0;
				lrchead->time.tv_sec=lrctime->minutes*60+lrctime->seconds;
				lrchead->time.tv_nsec=lrctime->microseconds*1000;
				if(head->oneline[count+1]!='['){
					lrchead->next=(struct lrctimeandchar *)malloc(sizeof(struct lrctimeandchar));
					lrchead=lrchead->next;
					break;
				}else{
					lrchead->next=(struct lrctimeandchar *)malloc(sizeof(struct lrctimeandchar));
					lrchead=lrchead->next;
					strcpy(lrchead->character,templine+1);
				}
			}
		}

	head=head->next;	
	}while(head->next!=NULL);

	templine=strrchr(head->oneline,']');
	strcpy(lrchead->character,templine+1);

	for(count=0;head->oneline[count]!='\0';count++){
		if(head->oneline[count]=='.'){
			dot=1;
			continue;
		}
		if(head->oneline[count]==']'){
			if(dot==0){
				strncpy(lrctimeghost,&(head->oneline[count-6]),6);
			}else{
				strncpy(lrctimeghost,&(head->oneline[count-9]),9);
			}

				lrctime=lrctimechar_to_lrctimeint(dot,lrctimeghost);
				dot=0;
				lrchead->time.tv_sec=lrctime->minutes*60+lrctime->seconds;
				lrchead->time.tv_nsec=lrctime->microseconds*1000;

			if(head->oneline[count+1]!='[')
				break;
			
		}
	}

	lrchead->next=NULL;
	do{
		head= headtemp->next;
		free(headtemp);
		headtemp=head;
	}while(headtemp->next!=NULL);
	free(headtemp);

	return temp;
}

struct lrctimeint * lrctimechar_to_lrctimeint(int dot, char *lrcptr)
{
	struct lrctimeint * newintstruct;
	newintstruct=(struct lrctimeint *)malloc(sizeof(struct lrctimeint));
	struct lrctimechar * newcharstruct;
	newcharstruct=(struct lrctimechar *)malloc(sizeof(struct lrctimechar));
	if(newintstruct==NULL||newcharstruct==NULL)
		return NULL;

	newcharstruct->minutes[0]=lrcptr[1];
	newcharstruct->minutes[1]=lrcptr[2];
	newcharstruct->minutes[2]='\0';

	newcharstruct->seconds[0]=lrcptr[4];
	newcharstruct->seconds[1]=lrcptr[5];
	newcharstruct->seconds[2]='\0';

	if(dot==1){
		newcharstruct->microseconds[0]=lrcptr[7];
		newcharstruct->microseconds[1]=lrcptr[8];
		newcharstruct->microseconds[2]='\0';
	}else{
		newcharstruct->microseconds[0]='0';
		newcharstruct->microseconds[1]='0';
		newcharstruct->microseconds[2]='\0';
	}

	newintstruct->minutes=atol(newcharstruct->minutes);
	newintstruct->seconds=atol(newcharstruct->seconds);
	newintstruct->microseconds=atol(newcharstruct->microseconds);

	free(newcharstruct);
	return newintstruct;
}
struct lrctimeandchar *copy_contain(struct lrctimeandchar *dst,struct lrctimeandchar *src)
{
	strcpy(dst->character,src->character);
	dst->time.tv_sec=src->time.tv_sec;
	dst->time.tv_nsec=src->time.tv_nsec;

	return dst;

}
struct lrctimeandchar *sort_lrcfile(struct lrctimeandchar *head)
{
	struct lrctimeandchar *headsorted,*tempdst1,*tempsrc2,*tempsrc1,*tempsrc3;
	int count;
	tempsrc2=head;
	tempsrc1=head;
	tempsrc3=head;
	int funcforqsort(const void *a,const void *b)
	{
		if(*(int *)a>*(int *)b)
			return 1;
		else if (*(int *)a<*(int *)b)
			return -1;
		else
			return 0;
	}


	for(count=0;tempsrc1->next!=NULL;count++,tempsrc1=tempsrc1->next);
	count++;
	tempsrc1=head;

	long int *timebuffer;
	timebuffer=(long int *)malloc(sizeof(long int)*count);
	int i;
	for(i=0;i<count;i++){
		timebuffer[i]=tempsrc2->time.tv_sec*1000000+tempsrc2->time.tv_nsec;
		if(tempsrc2->next!=NULL)
			tempsrc2=tempsrc2->next;
	}
	tempsrc2=tempsrc3;

	qsort((void *)timebuffer,count,sizeof(long int),funcforqsort);

	tempdst1=(struct lrctimeandchar *)malloc(sizeof(struct lrctimeandchar));
	headsorted=tempdst1;
	for(i=0;i<count;i++){
		if(i!=count-1){
			tempdst1->next=(struct lrctimeandchar *)malloc(sizeof(struct lrctimeandchar));
			tempdst1=tempdst1->next;

		}else{
			tempdst1->next=NULL;
		}
	}
	tempdst1=headsorted;

	for(i=0;i<count;i++){

		for(;;){
			if(tempsrc2->next!=NULL){
				if(timebuffer[i]==(tempsrc2->time.tv_sec*1000000+tempsrc2->time.tv_nsec)){
					strcpy(tempdst1->character,tempsrc2->character);
					tempdst1->time.tv_sec=tempsrc2->time.tv_sec;
					tempdst1->time.tv_nsec=tempsrc2->time.tv_nsec;
				}
				tempsrc2=tempsrc2->next;
			}else{
				if(timebuffer[i]==(tempsrc2->time.tv_sec*1000000+tempsrc2->time.tv_nsec)){
					strcpy(tempdst1->character,tempsrc2->character);
					tempdst1->time.tv_sec=tempsrc2->time.tv_sec;
					tempdst1->time.tv_nsec=tempsrc2->time.tv_nsec;
				}
				tempdst1=tempdst1->next;
				tempsrc2=head;
				break;
			}
		}
	}
	return headsorted;

}

struct lrctimeandchar *get_timetosleep(struct lrctimeandchar *head)
{
	struct lrctimeandchar *tempsrc1,*tempsrc2;

	tempsrc1=head;
	tempsrc2=head;

	struct lrctimeandchar *temphead,*temptail;
	temptail=(struct lrctimeandchar *)malloc(sizeof(struct lrctimeandchar));
	temphead=temptail;

	temptail->time.tv_sec=tempsrc1->time.tv_sec;
	temptail->time.tv_nsec=tempsrc1->time.tv_nsec;
	
	temptail->next=(struct lrctimeandchar *)malloc(sizeof(struct lrctimeandchar));
	temptail=temptail->next;

	tempsrc1=tempsrc1->next;

	do{
		temptail->time.tv_sec=tempsrc1->time.tv_sec;
		temptail->time.tv_nsec=tempsrc1->time.tv_nsec;
		tempsrc1=tempsrc1->next;
		temptail->next=(struct lrctimeandchar *)malloc(sizeof(struct lrctimeandchar));
		temptail=temptail->next;
	}while(tempsrc1->next!=NULL);

	temptail->next=NULL;
	temptail=temphead;
	tempsrc2=tempsrc2->next;
	do{
		if(tempsrc2->time.tv_nsec>=temphead->time.tv_nsec){
			tempsrc2->time.tv_sec=tempsrc2->time.tv_sec-temphead->time.tv_sec;
			tempsrc2->time.tv_nsec=tempsrc2->time.tv_nsec-temphead->time.tv_nsec;
		}else{
			tempsrc2->time.tv_sec=tempsrc2->time.tv_sec-1-temphead->time.tv_sec;
			tempsrc2->time.tv_nsec=1000000000+tempsrc2->time.tv_nsec-temphead->time.tv_nsec;
		}

		tempsrc2=tempsrc2->next;
		temphead=temphead->next;
	}while(tempsrc2->next!=NULL);
	if(tempsrc2->time.tv_nsec>=temphead->time.tv_nsec){
		tempsrc2->time.tv_sec=tempsrc2->time.tv_sec-temphead->time.tv_sec;
		tempsrc2->time.tv_nsec=tempsrc2->time.tv_nsec-temphead->time.tv_nsec;
	}else{
		tempsrc2->time.tv_sec=tempsrc2->time.tv_sec-1-temphead->time.tv_sec;
		tempsrc2->time.tv_nsec=1000000000+tempsrc2->time.tv_nsec-temphead->time.tv_nsec;
	}

	do{
		temphead=temptail->next;
		free(temptail);
		temptail=temphead;
	}while(temptail!=NULL);
	free(temptail);

	return head;
}
